package com.ideaaedi.commonmp.lite.transaction;

import com.baomidou.dynamic.datasource.aop.DynamicLocalTransactionInterceptor;
import com.baomidou.dynamic.datasource.tx.DsPropagation;
import com.baomidou.dynamic.datasource.tx.TransactionalExecutor;
import com.baomidou.dynamic.datasource.tx.TransactionalInfo;
import com.baomidou.dynamic.datasource.tx.TransactionalTemplate;
import com.ideaaedi.commonds.function.NoArgConsumer;
import com.ideaaedi.commonds.function.NoArgFunction;
import com.ideaaedi.commonds.transaction.ManualTransactionSupport;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.function.Consumer;
import java.util.function.Function;

/**
 * dynamic-datasource 手动事务支持 （即：等价于{@link com.baomidou.dynamic.datasource.annotation.DSTransactional}）
 * <p>
 * 见{@link DynamicLocalTransactionInterceptor}，版本：com.baomidou:dynamic-datasource-spring-boot-starter:4.2.0
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since @since 2100.8.0
 */
@Slf4j
public class DSManualTransactionSupport implements ManualTransactionSupport {
    
    private final TransactionalTemplate transactionalTemplate;
    
    private final TransactionalInfo transactionalInfo;
    
    protected DSManualTransactionSupport() {
        this.transactionalTemplate = new TransactionalTemplate();
        this.transactionalInfo = new TransactionalInfo();
        //noinspection unchecked
        this.transactionalInfo.setRollbackFor(new Class[]{Exception.class});
        //noinspection unchecked
        this.transactionalInfo.setNoRollbackFor(new Class[]{});
        this.transactionalInfo.setPropagation(DsPropagation.REQUIRED);
    }
    
    protected DSManualTransactionSupport(DsPropagation propagation) {
        this.transactionalTemplate = new TransactionalTemplate();
        this.transactionalInfo = new TransactionalInfo();
        //noinspection unchecked
        this.transactionalInfo.setRollbackFor(new Class[]{Exception.class});
        //noinspection unchecked
        this.transactionalInfo.setNoRollbackFor(new Class[]{});
        this.transactionalInfo.setPropagation(propagation);
    }
    
    protected DSManualTransactionSupport(Class<? extends Throwable>[] rollbackFor) {
        this.transactionalTemplate = new TransactionalTemplate();
        this.transactionalInfo = new TransactionalInfo();
        this.transactionalInfo.setRollbackFor(rollbackFor);
        //noinspection unchecked
        this.transactionalInfo.setNoRollbackFor(new Class[]{});
        this.transactionalInfo.setPropagation(DsPropagation.REQUIRED);
    }
    
    protected DSManualTransactionSupport(TransactionalInfo transactionalInfo) {
        this.transactionalTemplate = new TransactionalTemplate();
        this.transactionalInfo = transactionalInfo;
    }
    
    @Override
    @SneakyThrows
    public <P, R> R exec(Function<P, R> function, P param) {
        log.debug(" transactionalInfo -> {}", transactionalInfo);
        //noinspection unchecked
        return (R) transactionalTemplate.execute(new TransactionalExecutor() {
            @Override
            public Object execute() {
                return function.apply(param);
            }
            
            @Override
            public TransactionalInfo getTransactionInfo() {
                return transactionalInfo;
            }
        });
    }
    
    @Override
    @SneakyThrows
    public <R> R exec(NoArgFunction<R> function) {
        log.debug(" transactionalInfo -> {}", transactionalInfo);
        //noinspection unchecked
        return (R) transactionalTemplate.execute(new TransactionalExecutor() {
            @Override
            public Object execute() {
                return function.apply();
            }
            
            @Override
            public TransactionalInfo getTransactionInfo() {
                return transactionalInfo;
            }
        });
    }
    
    @Override
    @SneakyThrows
    public <P> void voidExec(Consumer<P> consumer, P param) {
        log.debug(" transactionalInfo -> {}", transactionalInfo);
        transactionalTemplate.execute(new TransactionalExecutor() {
            @Override
            public Object execute() {
                consumer.accept(param);
                return null;
            }
            
            @Override
            public TransactionalInfo getTransactionInfo() {
                return transactionalInfo;
            }
        });
    }
    
    @Override
    @SneakyThrows
    public void voidExec(NoArgConsumer consumer) {
        log.debug(" transactionalInfo -> {}", transactionalInfo);
        transactionalTemplate.execute(new TransactionalExecutor() {
            @Override
            public Object execute() {
                consumer.accept();
                return null;
            }
            
            @Override
            public TransactionalInfo getTransactionInfo() {
                return transactionalInfo;
            }
        });
    }
    
    public static DSManualTransactionSupport create() {
        return new DSManualTransactionSupport();
    }
    
    public static DSManualTransactionSupport create(DsPropagation propagation) {
        return new DSManualTransactionSupport(propagation);
    }
    
    public static DSManualTransactionSupport create(Class<? extends Throwable>[] rollbackFor) {
        return new DSManualTransactionSupport(rollbackFor);
    }
    
    public static DSManualTransactionSupport create(TransactionalInfo transactionalInfo) {
        return new DSManualTransactionSupport(transactionalInfo);
    }
}
